package com.swak.vertx.protocol.http.adapter;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

import com.swak.Constants;
import com.swak.exception.AccessDeniedException;
import com.swak.exception.ErrorCode;
import com.swak.security.Permission;
import com.swak.security.Subject;
import com.swak.vertx.invoker.MethodInvoker;

/**
 * 权限适配
 * 
 * @author lifeng
 */
public class PermissionAdapter extends AbstractAdapter {

	@Override
	public CompletionStage<Context> handle(Context context) {
		Subject subject = context.$context.get(Constants.SUBJECT_NAME);
		CompletionStage<Boolean> authFuture = this.checkPermissions(subject, context.$method);
		if (authFuture != null) {
			return authFuture.thenCompose(allow -> {
				if (allow) {
					return this.next(context);
				}
				throw new AccessDeniedException(ErrorCode.ACCESS_DENIED);
			}).exceptionally(e -> {
				context.$error = e;
				return context;
			});
		}
		return this.next(context);
	}

	/**
	 * 校验权限
	 */
	private CompletionStage<Boolean> checkPermissions(Subject subject, MethodInvoker handler) {

		// 优先校验权限
		Permission requiresRoles = handler.getRequiresRoles();
		Permission requiresPermissions = handler.getRequiresPermissions();

		// 如果定义了权限，则必须有用户
		if (subject == null && (requiresRoles != null || requiresPermissions != null)) {
			throw new AccessDeniedException(ErrorCode.ACCESS_DENIED);
		}

		// first
		CompletionStage<Boolean> authFuture = null;
		if (requiresPermissions != null && requiresPermissions != Permission.NONE) {
			authFuture = subject.isPermitted(requiresPermissions);
		}

		// return
		if (authFuture != null && requiresRoles != null && requiresRoles != Permission.NONE) {
			return authFuture.thenCompose(res -> {
				if (res) {
					return subject.hasRole(requiresRoles);
				}
				return CompletableFuture.completedFuture(false);
			});
		} else if (requiresRoles != null && requiresRoles != Permission.NONE) {
			authFuture = subject.hasRole(requiresRoles);
		}

		// permissions -> roles
		// permissions
		// roles
		// null
		return authFuture;
	}
}
